package com.cloudweb.oa.controller;

import cn.js.fan.db.ListResult;
import cn.js.fan.util.*;
import cn.js.fan.web.Global;
import cn.js.fan.web.SkinUtil;
import com.alibaba.fastjson.JSONArray;
import com.cloudweb.oa.api.*;
import com.cloudweb.oa.cache.FlowFormDaoCache;
import com.cloudweb.oa.cache.VisualFormDaoCache;
import com.cloudweb.oa.security.AuthUtil;
import com.cloudweb.oa.service.FormArchiveService;
import com.cloudweb.oa.service.FormService;
import com.cloudweb.oa.service.MacroCtlService;
import com.cloudweb.oa.utils.*;
import com.cloudweb.oa.vo.Result;
import com.cloudwebsoft.framework.db.JdbcTemplate;
import com.cloudwebsoft.framework.util.LogUtil;
import com.redmoon.oa.Config;
import com.redmoon.oa.LogDb;
import com.redmoon.oa.basic.SelectDb;
import com.redmoon.oa.basic.SelectMgr;
import com.redmoon.oa.flow.*;
import com.redmoon.oa.flow.macroctl.*;
import com.redmoon.oa.person.UserDb;
import com.redmoon.oa.pvg.Privilege;
import com.redmoon.oa.pvg.RoleDb;
import com.redmoon.oa.visual.ModuleRelateDb;
import com.redmoon.oa.visual.ModuleSetupDb;
import com.redmoon.oa.visual.ModuleUtil;
import io.swagger.annotations.*;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.xml.sax.InputSource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

@Api(tags = "表单")
@Controller
@RequestMapping("/form")
public class FormController {
    @Autowired
    private HttpServletRequest request;

    @Autowired
    private FlowFormDaoCache flowFormDaoCache;

    @Autowired
    private VisualFormDaoCache visualFormDaoCache;

    @Autowired
    private I18nUtil i18nUtil;

    @Autowired
    private MacroCtlService macroCtlService;

    @Autowired
    private FormService formService;

    @Autowired
    private AuthUtil authUtil;

    @Autowired
    private IFormProUtil formProUtil;

    @Autowired
    private IFormSchemaUtil formSchemaUtil;

    @Autowired
    private ILicenseUtil licenseUtil;

    @ApiOperation(value = "解析出表单的字段", notes = "解析出表单的字段", httpMethod = "POST")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/parseForm", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> parseForm() {
        String content = ParamUtil.get(request, "content");
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        try {
            json.put("ret", 1);
            json.put("fields", com.alibaba.fastjson.JSONArray.parseArray(FormParser.doParse(content)));
        } catch (ErrMsgException e) {
            json.put("ret", 0);
            json.put("msg", e.getMessage());
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(json);
    }

    @ApiOperation(value = "保存字段管理内容", notes = "保存字段管理内容", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "JSONObject"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/updateFields", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> updateFields(@RequestBody com.alibaba.fastjson.JSONObject jsonObject) {
        boolean re = false;
        String formCode = jsonObject.getString("formCode");
        JSONArray fields = jsonObject.getJSONArray("fields");
        try {
            FormDb fd = new FormDb();
            fd = fd.getFormDb(formCode);
            int orders = fields.size();
            for (Object o : fields) {
                com.alibaba.fastjson.JSONObject json = (com.alibaba.fastjson.JSONObject) o;
                String name = json.getString("name");
                String title = json.getString("title");
                String canNull = json.getString("canNull");
                String canQuery = json.getString("canQuery");
                String canList = json.getString("canList");
                String width = json.getString("width");
                String isMobileDisplay = json.getString("isMobileDisplay");
                String isHide = json.getString("isHide");
                String moreThan = json.getString("moreThan");
                String morethanMode = json.getString("morethanMode");
                int isUnique = json.getIntValue("isUnique");
                int isHelper = json.getIntValue("isHelper");
                boolean isReadonly = json.getBoolean("isReadonly");
                String readOnlyType = json.getString("readOnlyType");
                String defaultValue = json.getString("defaultValue");
                re = fd.updateFieldProps(title, canNull, canQuery, canList, String.valueOf(orders), width, isMobileDisplay, isHide, moreThan, morethanMode, isUnique, isHelper, formCode, name, isReadonly, readOnlyType, defaultValue);
                orders--;
            }

            if (re) {
                FormCache fc = new FormCache(fd);
                fd.getPrimaryKey().setValue(formCode);
                fc.refreshSave(fd.getPrimaryKey());
            }
        } catch (ErrMsgException e) {
            LogUtil.getLog(getClass()).error(e);
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "删除表单", notes = "删除表单", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "codes", value = "表单编码，以逗号分隔", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin')")
    @ResponseBody
    @RequestMapping(value = "/del", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> del(String codes) {
        try {
            FormMgr fm = new FormMgr();
            String[] ary = StrUtil.split(codes, ",");
            for (String code : ary) {
                fm.del(code);
            }
        } catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        return new Result<>();
    }

    @ApiOperation(value = "校验表单编码", notes = "校验表单编码", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin')")
    @ResponseBody
    @RequestMapping(value = "/checkCode", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> checkCode(@RequestParam String code) {
        code = code.trim().toLowerCase();

        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();

        if (!StrUtil.isSimpleCode(code)) {
            json.put("res", false);
            json.put("msg", "编码只能为字母、数字及符号：-_");
            return new Result<>(json);
        }

        if (code.contains("@")) {
            json.put("res", false);
            json.put("msg", "编码中不能含有@字符");
            return new Result<>(json);
        }

        if ("code".equals(code)) {
            json.put("res", false);
            json.put("msg", "编码不能为code");
            return new Result<>(json);
        }

        if ("id".equals(code)) {
            json.put("res", false);
            json.put("msg", "编码不能为id");
            return new Result<>(json);
        }

        FormDb fd = new FormDb();
        fd = fd.getFormDb(code);
        if (fd.isLoaded()) {
            json.put("res", false);
            json.put("msg", "编码不能重复");
            return new Result<>(json);
        }
        return new Result<>();
    }

    @ApiOperation(value = "为计算控件取得嵌套表单域中数值型或SQL宏控件型的字段", notes = "为计算控件取得嵌套表单域中数值型或SQL宏控件型的字段", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "macroType", value = "宏控件类型", required = true, dataType = "String"),
            @ApiImplicitParam(name = "defaultValue", value = "默认值", required = true, dataType = "String"),
            @ApiImplicitParam(name = "description", value = "控件描述", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getNestFieldsForCal", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getNestFieldsForCal(String macroType, String defaultValue, String description) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        JSONArray arr = new JSONArray();
        MacroCtlMgr mm = new MacroCtlMgr();
        MacroCtlUnit mu = mm.getMacroCtlUnit(macroType);
        if (mu.getNestType() != MacroCtlUnit.NEST_TYPE_NONE) {
            String nestFormCode = defaultValue;
            try {
                if (mu.getNestType() == MacroCtlUnit.NEST_DETAIL_LIST) {
                    defaultValue = StrUtil.decodeJSON(description);
                } else {
                    /*String desc = defaultValue;
                    if ("".equals(desc)) {
                        desc = description;
                    }*/
                    defaultValue = StrUtil.decodeJSON(description);
                }
                // 20131123 fgf 添加
                JSONObject jsonObj = new JSONObject(defaultValue);
                nestFormCode = jsonObj.getString("destForm");
            } catch (JSONException e) {
                LogUtil.getLog(getClass()).info("defaultValue=" + defaultValue);
                LogUtil.getLog(getClass()).error(e);
                // LogUtil.getLog(getClass()).info(nestFormCode + " is old version before 20131123.");
            }

            // 20200628 改为支持副模块
            FormDb nestfd = new FormDb();
            nestfd = nestfd.getFormDb(nestFormCode);
            if (!nestfd.isLoaded()) {
                ModuleSetupDb msd = new ModuleSetupDb();
                msd = msd.getModuleSetupDb(nestFormCode);
                if (msd == null) {
                    LogUtil.getLog(getClass()).error("模块: " + nestFormCode + " 不存在");
                } else {
                    nestFormCode = msd.getString("form_code");
                    nestfd = nestfd.getFormDb(nestFormCode);
                }
            }

            for (FormField ff2 : nestfd.getFields()) {
                int fieldType = ff2.getFieldType();
                boolean isFound = false;
                if (fieldType == FormField.FIELD_TYPE_INT || fieldType == FormField.FIELD_TYPE_LONG || fieldType == FormField.FIELD_TYPE_FLOAT || fieldType == FormField.FIELD_TYPE_DOUBLE || fieldType == FormField.FIELD_TYPE_PRICE) {
                    isFound = true;
                }
                if (FormField.TYPE_MACRO.equals(ff2.getType())) {
                    mu = mm.getMacroCtlUnit(ff2.getMacroType());
                    if (mu.getIFormMacroCtl() instanceof ISQLCtl || mu.getIFormMacroCtl() instanceof IFormulaCtl) {
                        isFound = true;
                    }
                }

                if (isFound) {
                    com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject();
                    obj.put("name", ff2.getName());
                    obj.put("title", ff2.getTitle());
                    obj.put("type", ff2.getType());
                    obj.put("fieldType", ff2.getFieldType());
                    obj.put("macroType", ff2.getMacroType());
                    obj.put("formCode", nestFormCode);
                    obj.put("formName", nestfd.getName());
                    arr.add(obj);
                }
            }
            json.put("ret", "1");
            json.put("jsonAry", arr);
        } else {
            json.put("ret", "0");
            json.put("msg", "非嵌套表宏控件");
        }

        return new Result<>(json);
    }

    @ApiOperation(value = "取得控件的类型", notes = "取得控件的类型", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "fieldName", value = "字段名", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldType", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> getFieldType(String formCode, String fieldName) {
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        if (!fd.isLoaded()) {
            json.put("ret", 0);
            json.put("msg", "表单：" + formCode + "不存在");
            return new Result<>(json);
        }

        FormField ff = fd.getFormField(fieldName);
        if (ff == null) {
            json.put("ret", 0);
            json.put("msg", "表单：" + formCode + " 中的字段：" + fieldName + " 不存在");
            return new Result<>(json);
        }

        int fieldType;
        if (ff.getType().equals(FormField.TYPE_MACRO)) {
            MacroCtlMgr mm = new MacroCtlMgr();
            MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
            fieldType = mu.getIFormMacroCtl().getFieldType(ff);
        } else {
            fieldType = ff.getFieldType();
        }

        json.put("ret", 1);
        json.put("fieldType", fieldType);
        return new Result<>(json);
    }

    @ApiOperation(value = "导出表单", notes = "导出表单", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @RequestMapping("/exportForm")
    public void exportForm(HttpServletResponse response, String formCode) throws IOException, ErrMsgException {
        Privilege privilege = new Privilege();
        if (!privilege.isUserPrivValid(request, "read")) {
            throw new ErrMsgException(cn.js.fan.web.SkinUtil.LoadString(request, "pvg_invalid"));
        }


        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        if (!fd.isLoaded()) {
            throw new ErrMsgException("表单不存在！");
        }

        String encoding = System.getProperty("file.encoding");
        String content = "<meta http-equiv='Content-Type' content='text/html; charset=" + encoding + "'>";
        content = content + fd.getContent();

        response.setContentType(MIMEMap.get("html"));
        response.setHeader("Content-disposition", "attachment; filename=" + StrUtil.GBToUnicode(fd.getName()) + ".html");

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            bis = new BufferedInputStream(new ByteArrayInputStream(content.getBytes()));
            bos = new BufferedOutputStream(response.getOutputStream());

            byte[] buff = new byte[2048];
            int bytesRead;

            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        } catch (final IOException e) {
            LogUtil.getLog(getClass()).error(e);
        } finally {
            if (bis != null) {
                bis.close();
            }
            if (bos != null) {
                bos.close();
            }
        }
    }

    @ApiOperation(value = "取得模块的表单中的所有字段", notes = "取得模块的表单中的所有字段", httpMethod = "POST")
    @ApiImplicitParam(name = "moduleCode", value = "模块编码", required = true, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsByModuleCode", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsByModuleCode(@RequestParam String moduleCode) {
        ModuleSetupDb msd = new ModuleSetupDb();
        msd = msd.getModuleSetupDbOrInit(moduleCode);
        if (!msd.isLoaded()) {
            return new Result<>(false, "模块: " + moduleCode + " 不存在");
        }
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(msd.getString("form_code"));
        if (!formDb.isLoaded()) {
            return new Result<>(false, "模块" + msd.getString("name") + " 的表单: " + msd.getString("form_code") + " 不存在");
        }
        return new Result<>(formDb.getFields());
    }

    @ApiOperation(value = "取得流程表单中的所有字段", notes = "取得流程表单中的所有字段", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型编码", required = true, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsByFlowTypeCode", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsByFlowTypeCode(@RequestParam String typeCode) {
        com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);
        if (!lf.isLoaded()) {
            return new Result<>(false, "流程: " + typeCode + " 不存在");
        }
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(lf.getFormCode());
        if (!formDb.isLoaded()) {
            return new Result<>(false, "流程 " + typeCode + " 的表单: " + lf.getFormCode()+ " 不存在");
        }
        jsonObject.put("formCode", lf.getFormCode());
        jsonObject.put("fields", formDb.getFields());
        return new Result<>(jsonObject);
    }

    @ApiOperation(value = "取得流程中嵌套表的所有字段", notes = "取得流程中嵌套表的所有字段", httpMethod = "POST")
    @ApiImplicitParam(name = "typeCode", value = "流程类型编码", required = true, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getNestFieldsByTypeCode", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getNestFieldsByTypeCode(String typeCode) {
        com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
        Leaf lf = new Leaf();
        lf = lf.getLeaf(typeCode);
        if (!lf.isLoaded()) {
            return new Result<>(false, "流程: " + typeCode + " 不存在");
        }

        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(lf.getFormCode());
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + lf.getFormCode() + " 不存在");
        }


        Vector<FormField> v = new Vector<>();
        for (FormField ff : formDb.getFields()) {
            if ("nest_table".equals(ff.getMacroType()) || "nest_sheet".equals(ff.getMacroType())) {
                String nestFormCode = ff.getDefaultValue();
                try {
                    String defaultVal = StrUtil.decodeJSON(ff.getDescription());
                    JSONObject json = new JSONObject(defaultVal);
                    nestFormCode = json.getString("destForm");
                } catch (JSONException e) {
                    LogUtil.getLog(getClass()).info(e);
                }

                FormDb nestfd = new FormDb();
                nestfd = nestfd.getFormDb(nestFormCode);
                v.addAll(nestfd.getFields());

                jsonObject.put("nestFormCode", nestFormCode);
                jsonObject.put("nestFormFields", v);
                break;
            }
        }
        return new Result<>(jsonObject);
    }

    @ApiOperation(value = "取得表单中的所有字段", notes = "取得表单中的所有字段", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", required = false, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFields", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFields(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            ModuleSetupDb msd = new ModuleSetupDb();
            msd = msd.getModuleSetupDb(formCode);
            if (msd == null) {
                return new Result<>(false, "表单: " + formCode + " 不存在");
            }
            formDb = formDb.getFormDb(msd.getFormCode());
        }
        return new Result<>(formDb.getFields());
    }

    @ApiOperation(value = "取得表单及其关联模块中的所有字段", notes = "取得表单及其关联模块中的所有字段", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsWithRelated", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsWithRelated(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }
        Vector<FormField> v = formDb.getFields();

        ModuleRelateDb mrd = new ModuleRelateDb();
        for (ModuleRelateDb moduleRelateDb : mrd.getModulesRelated(formCode)) {
            mrd = moduleRelateDb;
            String relateCode = mrd.getString("relate_code");
            FormDb fd2 = new FormDb(relateCode);

            for (FormField ff : fd2.getFields()) {
                ff.setName("nest." + relateCode + "." + ff.getName());
                ff.setTitle(fd2.getName() + ": " + ff.getTitle());
                v.add(ff);
            }
        }
        return new Result<>(v);
    }

    @ApiOperation(value = "取得表单中的所有字段用于字段管理", notes = "取得表单中的所有字段用于字段管理", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsForManage", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsForManage(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }

        com.alibaba.fastjson.JSONArray arr = new com.alibaba.fastjson.JSONArray();
        for (FormField ff : formDb.getFields()) {
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("orders", ff.getOrders());
            json.put("fieldName", ff.getName());
            json.put("title", ff.getTitle());
            json.put("canNull", ff.isCanNull() ? 1 : 0);
            json.put("fieldType", ff.getFieldTypeDesc());
            json.put("defaultType", ff.getDefaultType());
            json.put("defaultValue", ff.getDefaultValueRaw());
            json.put("ctlType", FormField.getTypeDesc(ff.getType(), ff.getMacroType()));
            json.put("canQuery", ff.getQueryMode());
            json.put("canList", ff.isCanList() ? 1 : 0);
            json.put("macroType", ff.getMacroType());
            json.put("isReadonly", ff.isReadonly());
            json.put("readOnlyType", ff.getReadOnlyType());
            if (ff.getType().equals(FormField.TYPE_MACRO)) {
                if ("nest_table".equals(ff.getMacroType()) || "nest_sheet".equals(ff.getMacroType())) {
                    String defaultVal = StrUtil.decodeJSON(ff.getDescription());
                    JSONObject jsonObj;
                    try {
                        jsonObj = new JSONObject(defaultVal);
                        String nestModuleCode = jsonObj.getString("destForm");
                        String sourceModuleCode = jsonObj.getString("sourceForm");

                        ModuleSetupDb nestMsd = new ModuleSetupDb();
                        nestMsd = nestMsd.getModuleSetupDb(nestModuleCode);
                        if (nestMsd != null) {
                            FormDb nestFd = new FormDb();
                            nestFd = nestFd.getFormDb(nestMsd.getFormCode());
                            json.put("nestFormCode", nestMsd.getFormCode());
                            json.put("nestFormName", nestFd.getName());
                            json.put("nestModuleCode", nestModuleCode);
                            json.put("nestModuleName", nestMsd.getString("name"));
                        }

                        if (!"".equals(sourceModuleCode)) {
                            ModuleSetupDb sourceMsd = new ModuleSetupDb();
                            sourceMsd = sourceMsd.getModuleSetupDb(sourceModuleCode);
                            if (sourceMsd != null) {
                                FormDb sourceFd = new FormDb();
                                sourceFd = sourceFd.getFormDb(sourceMsd.getFormCode());
                                json.put("sourceFormCode", sourceMsd.getFormCode());
                                json.put("sourceFormName", sourceFd.getName());
                                json.put("sourceModuleCode", sourceModuleCode);
                                json.put("sourceModuleName", sourceMsd.getString("name"));
                            }
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
                else if ("module_field_select".equals(ff.getMacroType())) {
                    String strDesc = StrUtil.getNullStr(ff.getDescription());
                    // 向下兼容
                    if ("".equals(strDesc)) {
                        strDesc = ff.getDefaultValueRaw();
                    }
                    MacroCtlService macroCtlService = SpringUtil.getBean(MacroCtlService.class);
                    IModuleFieldSelectCtl moduleFieldSelectCtl = macroCtlService.getModuleFieldSelectCtl();
                    strDesc = moduleFieldSelectCtl.formatJSONString(strDesc);
                    com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(strDesc);
                    String moduleCode = jsonObject.getString("sourceFormCode");
                    if (moduleCode == null) {
                        LogUtil.getLog(getClass()).error(ff.getTitle() + " 未选择源表单");
                    } else {
                        ModuleSetupDb msd = new ModuleSetupDb();
                        msd = msd.getModuleSetupDb(moduleCode);
                        if (msd != null) {
                            FormDb sourceFd = new FormDb();
                            sourceFd = sourceFd.getFormDb(msd.getFormCode());
                            json.put("sourceFormCode", msd.getFormCode());
                            json.put("sourceFormName", sourceFd.getName());
                            json.put("sourceModuleCode", moduleCode);
                            json.put("sourceModuleName", msd.getName());
                        }
                    }
                }
            }

            if (ff.isUnique()) {
                json.put("isUnique", FormField.UNIQUE_GLOBAL);
            } else if (ff.isUniqueNest()) {
                json.put("isUnique", FormField.UNIQUE_NEST);
            } else {
                json.put("isUnique", FormField.UNIQUE_NONE);
            }

            json.put("isMobileDisplay", ff.isMobileDisplay() ? 1 : 0);
            json.put("width", ff.getWidth());
            json.put("isHelper", ff.isHelper() ? 1 : 0);
            json.put("isHide", ff.getHide());
            json.put("morethanMode", ff.getMorethanMode());
            json.put("moreThan", ff.getMoreThan());
            json.put("type", ff.getType());

            json.put("name", ff.getName());
            json.put("formCode", ff.getFormCode());
            arr.add(json);
        }
        return new Result<>(arr);
    }

    @ApiOperation(value = "取得表单中的树型基础数据字段", notes = "取得表单中的树型基础数据字段", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", required = false, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsByBasicTree", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsByBasicTree(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }

        SelectMgr sm = new SelectMgr();
        MacroCtlMgr mm = new MacroCtlMgr();
        IBasicSelectCtl basicSelectCtl = macroCtlService.getBasicSelectCtl();
        Vector<FormField> vt = new Vector<>();
        for (FormField ff : formDb.getFields()) {
            if (ff.getType().equals(FormField.TYPE_MACRO)) {
                MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                if (mu != null && ("macro_flow_select".equals(mu.getCode()) || "macro_basic_tree_select_ctl".equals(mu.getCode()))) {
                    String basicCode = basicSelectCtl.getCode(ff);
                    SelectDb sd = sm.getSelect(basicCode);
                    if (sd.getType() == SelectDb.TYPE_TREE) {
                        vt.addElement(ff);
                    }
                }
            }
        }
        return new Result<>(vt);
    }

    @ApiOperation(value = "取得表单中的所有数值型字段", notes = "取得表单中的所有数值型字段", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsByNumeric", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsByNumeric(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }
        Vector<FormField> v = formDb.getFields();
        Vector<FormField> vt = new Vector<>();
        for (FormField formField : v) {
            if (FormField.isNumeric(formField.getFieldType()) || FormField.TYPE_DATE.equals(formField.getType()) || FormField.TYPE_DATE_TIME.equals(formField)
                    || "macro_formula_ctl".equals(formField.getMacroType()) || "macro_sql".equals(formField.getMacroType())) {
                vt.add(formField);
            }
        }
        return new Result<>(vt);
    }

    @ApiOperation(value = "取得嵌套表中的所有数值型字段", notes = "取得嵌套表中的所有数值型字段", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getNestFieldsByNumeric", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getNestFieldsByNumeric(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }
        JSONArray arr = new JSONArray();
        Vector<FormField> v = formDb.getFields();
        for (FormField formField : v) {
            String macroType = formField.getMacroType();
            MacroCtlMgr mm = new MacroCtlMgr();
            MacroCtlUnit mu = mm.getMacroCtlUnit(macroType);
            if (mu == null) {
                LogUtil.getLog(getClass()).error("宏控件类型: " + macroType + " 不存在");
                continue;
            }
            if (mu.getNestType() != MacroCtlUnit.NEST_TYPE_NONE) {
                String defaultValue = formField.getDefaultValueRaw();
                String description = formField.getDescription();
                String nestFormCode = defaultValue;
                try {
                    if (mu.getNestType() == MacroCtlUnit.NEST_DETAIL_LIST) {
                        defaultValue = StrUtil.decodeJSON(description);
                    } else {
                        defaultValue = StrUtil.decodeJSON(description);
                    }
                    JSONObject jsonObj = new JSONObject(defaultValue);
                    nestFormCode = jsonObj.getString("destForm");
                } catch (JSONException e) {
                    LogUtil.getLog(getClass()).info("defaultValue=" + defaultValue);
                    LogUtil.getLog(getClass()).error(e);
                }

                // 20200628 改为支持副模块
                FormDb nestfd = new FormDb();
                nestfd = nestfd.getFormDb(nestFormCode);
                if (!nestfd.isLoaded()) {
                    ModuleSetupDb msd = new ModuleSetupDb();
                    msd = msd.getModuleSetupDb(nestFormCode);
                    if (msd == null) {
                        LogUtil.getLog(getClass()).error("模块: " + nestFormCode + " 不存在");
                    } else {
                        nestFormCode = msd.getString("form_code");
                        nestfd = nestfd.getFormDb(nestFormCode);
                    }
                }

                for (FormField ff2 : nestfd.getFields()) {
                    int fieldType = ff2.getFieldType();
                    boolean isFound = false;
                    if (fieldType == FormField.FIELD_TYPE_INT || fieldType == FormField.FIELD_TYPE_LONG || fieldType == FormField.FIELD_TYPE_FLOAT || fieldType == FormField.FIELD_TYPE_DOUBLE || fieldType == FormField.FIELD_TYPE_PRICE) {
                        isFound = true;
                    }
                    if (FormField.TYPE_MACRO.equals(ff2.getType())) {
                        mu = mm.getMacroCtlUnit(ff2.getMacroType());
                        if (mu.getIFormMacroCtl() instanceof ISQLCtl || mu.getIFormMacroCtl() instanceof IFormulaCtl) {
                            isFound = true;
                        }
                    }

                    if (isFound) {
                        com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject();
                        obj.put("name", ff2.getName());
                        obj.put("title", ff2.getTitle());
                        obj.put("type", ff2.getType());
                        obj.put("fieldType", ff2.getFieldType());
                        obj.put("macroType", ff2.getMacroType());
                        obj.put("formCode", nestFormCode);
                        obj.put("formName", nestfd.getName());
                        arr.add(obj);
                    }
                }
            }
        }
        return new Result<>(arr);
    }

    @ApiOperation(value = "取得表单中的所有日期型字段", notes = "取得表单中的所有日期型字段", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", required = false, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsByDate", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsByDate(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }
        Vector<FormField> v = formDb.getFields();
        Vector<FormField> vt = new Vector<>();
        for (FormField formField : v) {
            if (formField.getFieldType() == FormField.FIELD_TYPE_DATETIME || formField.getFieldType() == FormField.FIELD_TYPE_DATE) {
                vt.add(formField);
            }
        }
        return new Result<>(vt);
    }

    @ApiOperation(value = "取得所有宏控件", notes = "取得所有宏控件", httpMethod = "POST")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getMacroUnits", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getMacroUnits() {
        MacroCtlMgr mm = new MacroCtlMgr();
        Vector v = mm.getAllMacroUnit();
        Iterator ir = v.iterator();
        while (ir.hasNext()) {
            MacroCtlUnit mu = (MacroCtlUnit) ir.next();
            if (!mu.isDisplay()) {
                ir.remove();
            }
        }
        return new Result<>(v);
    }

    @ApiOperation(value = "取得主表中嵌套表的表单编码", notes = "取得主表中嵌套表的表单编码", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getNestFormCode", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getNestFormCode(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }

        String nestFormCode = "";
        for (FormField ff : formDb.getFields()) {
            if ("nest_table".equals(ff.getMacroType()) || "nest_sheet".equals(ff.getMacroType())) {
                nestFormCode = ff.getDefaultValue();
                try {
                    String defaultVal = StrUtil.decodeJSON(ff.getDescription());
                    JSONObject json = new JSONObject(defaultVal);
                    nestFormCode = json.getString("destForm");
                } catch (JSONException e) {
                    LogUtil.getLog(getClass()).info(e);
                }
                break;
            }
        }
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("nestFormCode", nestFormCode);
        return new Result<>(json);
    }

    @ApiOperation(value = "取得主表中嵌套表的所有字段", notes = "取得主表中嵌套表的所有字段，用于流程节点上表单中的用户", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", required = false, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getNestFields", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getNestFields(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }

        Vector<FormField> v = new Vector<>();
        for (FormField ff : formDb.getFields()) {
            if ("nest_table".equals(ff.getMacroType()) || "nest_sheet".equals(ff.getMacroType())) {
                String nestFormCode = ff.getDefaultValue();
                try {
                    String defaultVal = StrUtil.decodeJSON(ff.getDescription());
                    JSONObject json = new JSONObject(defaultVal);
                    nestFormCode = json.getString("destForm");
                } catch (JSONException e) {
                    LogUtil.getLog(getClass()).info(e);
                }

                FormDb nestfd = new FormDb();
                nestfd = nestfd.getFormDb(nestFormCode);
                v.addAll(nestfd.getFields());
                break;
            }
        }
        return new Result<>(v);
    }

    @ApiOperation(value = "取得主表中嵌套表的所有字段（含表单名称）", notes = "取得主表中嵌套表的所有字段，用于回写界面的配置", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", required = false, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getNestFieldsWithFormName", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getNestFieldsWithFormName(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }

        JSONArray ary = new JSONArray();
        for (FormField ff : formDb.getFields()) {
            if ("nest_table".equals(ff.getMacroType()) || "nest_sheet".equals(ff.getMacroType())) {
                String nestFormCode = ff.getDefaultValue();
                try {
                    String defaultVal = StrUtil.decodeJSON(ff.getDescription());
                    JSONObject json = new JSONObject(defaultVal);
                    nestFormCode = json.getString("destForm");
                } catch (JSONException e) {
                    LogUtil.getLog(getClass()).info(e);
                }

                FormDb nestfd = new FormDb();
                nestfd = nestfd.getFormDb(nestFormCode);
                Vector<FormField> v = nestfd.getFields();
                for (FormField nestFf : v) {
                    com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                    json.put("name", nestFf.getName());
                    json.put("title", nestFf.getTitle());
                    json.put("formCode", nestFf.getFormCode());
                    json.put("formName", nestfd.getName());
                    json.put("nestFieldName", ff.getName());
                    ary.add(json);
                }
            }
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "取得表单中部门型的字段", notes = "取得表单中部门型的字段，用于限定部门表单域及流程节点上提醒人员配置", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsOfDept", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsOfDept(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }

        Vector<FormField> v = new Vector<>();
        for (FormField ff : formDb.getFields()) {
            if ("macro_dept_select".equals(ff.getMacroType()) || "macro_my_dept_select".equals(ff.getMacroType())
                    || "macro_dept_sel_win".equals(ff.getMacroType()) || "macro_moredeptctl".equals(ff.getMacroType())
            ) {
                v.addElement(ff);
            }
        }
        return new Result<>(v);
    }

    @ApiOperation(value = "取得表单中部门选择框型的字段", notes = "取得表单中部门选择框型的字段，用于用户选择框的联动", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsOfDeptSelectCtl", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsOfDeptSelectCtl(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }

        Vector<FormField> v = new Vector<>();
        for (FormField ff : formDb.getFields()) {
            if ("macro_dept_select".equals(ff.getMacroType())) {
                v.addElement(ff);
            }
        }
        return new Result<>(v);
    }

    @ApiOperation(value = "取得表单中用户型的字段", notes = "取得表单中用户型的字段，用于流程节点上提醒人员配置", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFieldsOfUser", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getFieldsOfUser(String formCode) {
        FormDb formDb = new FormDb();
        formDb = formDb.getFormDb(formCode);
        if (!formDb.isLoaded()) {
            return new Result<>(false, "表单: " + formCode + " 不存在");
        }

        Vector<FormField> v = new Vector<>();
        for (FormField ff : formDb.getFields()) {
            String macroType = ff.getMacroType();
            if ("macro_current_user".equals(macroType)
                    || "macro_user_select".equals(macroType)
                    || "role_user_select".equals(macroType)
                    || "macro_user_select_win".equals(macroType)
                    || "macro_user_multi_select_win".equals(macroType)
                    || "macro_depts_user_multi_sel_win".equals(macroType)) {
                v.addElement(ff);
            }
        }
        return new Result<>(v);
    }

    @ApiOperation(value = "取得表单的视图", notes = "取得表单的视图", httpMethod = "POST")
    @ApiImplicitParam(name = "formCode", value = "表单编码", required = false, dataType = "String")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getViews", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> getViews(String formCode) {
        JSONArray ary = new JSONArray();
        FormViewDb fvd = new FormViewDb();
        List list = fvd.getViews(formCode);
        for (Object o : list) {
            FormViewDb formViewDb = (FormViewDb) o;
            com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
            jsonObject.put("id", formViewDb.getLong("id"));
            jsonObject.put("name", formViewDb.getString("name"));
            ary.add(jsonObject);
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "拷贝表单字段的值", notes = "拷贝表单字段的值", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", dataType = "String"),
            @ApiImplicitParam(name = "sourceFieldName", value = "源字段", dataType = "String"),
            @ApiImplicitParam(name = "targetFieldName", value = "目标字段", dataType = "String")
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/copyField", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;"})
    public Result<Object> copyField(String formCode, String sourceFieldName, String targetFieldName) {
        String sql = "update ft_" + formCode + " set " + targetFieldName + "=" + sourceFieldName;
        JdbcTemplate jt = new JdbcTemplate();
        try {
            jt.executeUpdate(sql);
            flowFormDaoCache.refreshAll(formCode);
            visualFormDaoCache.refreshAll(formCode);
        } catch (SQLException e) {
            LogUtil.getLog(getClass()).error(e);
        }
        return new Result<>(true);
    }

    @ApiOperation(value = "列出流程类型下的表单", notes = "列出流程类型下的表单", httpMethod = "POST")
    @ApiImplicitParam(name = "code", value = "流程类型编码", dataType = "String")
    @RequestMapping(value = "/listByFlowType", produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    @ResponseBody
    public Result<Object> listByFlowType(String code) {
        FormDb fd = new FormDb();
        Vector<FormDb> formV = fd.listOfFlow(code);
        com.alibaba.fastjson.JSONArray arr = new JSONArray();
        for (FormDb formDb : formV) {
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("code", formDb.getCode());
            json.put("name", formDb.getName());
            arr.add(json);
        }
        return new Result<>(arr);
    }

    @ApiOperation(value = "列出表单", notes = "列出表单", httpMethod = "POST")
    @RequestMapping(value = "/list", produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    @ResponseBody
    public Result<Object> list() {
        FormDb fd = new FormDb();
        Vector<FormDb> formV = fd.list();
        com.alibaba.fastjson.JSONArray arr = new JSONArray();
        for (FormDb formDb : formV) {
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("code", formDb.getCode());
            json.put("name", formDb.getName());
            arr.add(json);
        }
        return new Result<>(arr);
    }

    @ApiOperation(value = "取得表单中的元素，包括控件、表格行、span、div", notes = "取得表单中的元素，包括控件、表格行、span、div", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "typeCode", value = "流程类型", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getFormElements", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> getFormElements(String formCode) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);

        com.alibaba.fastjson.JSONArray arr = new JSONArray();
        // 解析表单中的元素，包括控件、表格行、span、div
        org.json.JSONArray ary = FormParser.getElements(request, fd);
        try {
            for (int i = 0; i < ary.length(); i++) {
                JSONObject json = ary.getJSONObject(i);
                String objId = json.getString("id");
                String objName = json.getString("name");
                if ("".equals(objId)) {
                    objId = objName;
                }
                String objTitle = json.getString("title").replaceAll("&nbsp;", "").trim();
                if (!"".equals(objName)) {
                    objTitle = objName;
                } else {
                    if ("".equals(objTitle)) {
                        objTitle = objId;
                    }
                }

                String tagName = null;
                tagName = json.getString("tagName");

                if ("tr".equals(tagName)) {
                    tagName = "表格行";
                } else if ("input".equals(tagName)) {
                    tagName = "输入框";
                } else if ("textarea".equals(tagName)) {
                    tagName = "多行输入框";
                } else if ("select".equals(tagName)) {
                    tagName = "下拉菜单";
                } else if ("checkbox".equals(tagName)) {
                    tagName = "复选框";
                } else if ("radio".equals(tagName)) {
                    tagName = "单选框";
                } else if ("table".equals(tagName)) {
                    tagName = "表格";
                }
                com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
                jsonObject.put("id", objId);
                jsonObject.put("tagName", tagName.toUpperCase());
                jsonObject.put("title", objTitle);
                arr.add(jsonObject);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return new Result<>(arr);
    }

    @ApiOperation(value = "列出表单（带分页、查询）", notes = "列出表单（带分页、查询）", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", value = "页码", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, dataType = "Integer"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/listForm", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> listForm(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "20") Integer pageSize) {
        FormDb fd = new FormDb();
        String sql = fd.getSqlList(request);

        Directory dir = new Directory();
        ListResult lr = fd.listResult(sql, page, pageSize);

        JSONArray arr = new JSONArray();
        for (Object o : lr.getResult()) {
            fd = (FormDb) o;
            com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
            json.put("code", fd.getCode());
            json.put("name", fd.getName());
            json.put("isFlow", fd.isFlow());
            json.put("tableName", fd.getTableName());
            json.put("isLog", fd.isLog()?"是":"否");
            json.put("isHasAttachment", fd.isHasAttachment()?"是":"否");
            Leaf lf = dir.getLeaf(fd.getFlowTypeCode());
            if (lf != null) {
                json.put("typeName", lf.getName());
            }
            arr.add(json);
        }

        com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
        jsonObject.put("list", arr);
        jsonObject.put("page", page);
        jsonObject.put("total", lr.getTotal());
        return new Result<>(jsonObject);
    }

    @ApiOperation(value = "添加表单页面", notes = "添加表单页面", httpMethod = "POST")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin')")
    @ResponseBody
    @RequestMapping(value = "/addFormPage", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> addFormPage() {
        com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
        Config cfg = Config.getInstance();
        int cfgPageStyle = cfg.getInt("pageStyle");
        jsonObject.put("cfgPageStyle", cfgPageStyle);
        return new Result<>(jsonObject);
    }

    @ApiOperation(value = "编辑表单页面", notes = "编辑表单页面", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin')")
    @ResponseBody
    @RequestMapping(value = "/editFormPage", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> editFormPage(@RequestParam String code) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(code);

        com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
        jsonObject.put("name", fd.getName());
        jsonObject.put("isLog", fd.isLog());
        jsonObject.put("hasAttachment", fd.isHasAttachment());
        jsonObject.put("isOnlyCamera", fd.isOnlyCamera());
        jsonObject.put("isFlow", fd.isFlow());
        jsonObject.put("flowTypeCode", fd.getFlowTypeCode());
        jsonObject.put("pageStyle", fd.getPageStyle());

        Config cfg = Config.getInstance();
        int cfgPageStyle = cfg.getInt("pageStyle");
        jsonObject.put("cfgPageStyle", cfgPageStyle);

        jsonObject.put("unitCode", fd.getUnitCode());
        jsonObject.put("code", fd.getCode());
        jsonObject.put("dataSource", fd.getDataSource());
        jsonObject.put("formTable", fd.getFormTable());
        jsonObject.put("isRelate", fd.isRelate() ? "1" : "0");
        jsonObject.put("viewRuleMode", fd.getViewRuleMode() == ConstUtil.VIEW_RULE_MODE_EMPTY);
        jsonObject.put("isRendWithoutViewRule", fd.isRendWithoutViewRule());
        return new Result<>(jsonObject);
    }

    @ApiOperation(value = "保存表单属性", notes = "保存表单属性", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "name", value = "表单名称", required = true, dataType = "String"),
            @ApiImplicitParam(name = "isLog", value = "是否保留置历史记录", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "hasAttachment", value = "是否带有附件", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "isOnlyCamera", value = "手机端只允许拍照，不能选择照片上传", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "isFlow", value = "是否流程型", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "flowTypeCode", value = "流程类型编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "pageStyle", value = "页面风格", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "unitCode", value = "单位编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "viewRuleMode", value = "应用显示规则时清空隐藏元素中的值", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "dataSource", value = "数据源", required = true, dataType = "String"),
            @ApiImplicitParam(name = "formTable", value = "关联的表格", required = true, dataType = "String"),
            @ApiImplicitParam(name = "isRelate", value = "是否关联已有表", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "isRendWithoutViewRule", value = "显示规则是否分离渲染", required = true, dataType = "Boolean"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/updateForm", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> updateForm(@RequestParam String code, @RequestParam String name,
                                     @RequestParam Boolean isLog, Boolean hasAttachment, Boolean isOnlyCamera,
                                     Boolean isFlow, String flowTypeCode, Integer pageStyle, String unitCode,
                                     @RequestParam(defaultValue = "false") Boolean viewRuleMode,
                                     String dataSource, String formTable, @RequestParam(defaultValue = "false") Boolean isRelate,
                                     @RequestParam(defaultValue = "false") boolean isRendWithoutViewRule
    ) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(code);

        if (!fd.isLoaded()) {
            return new Result<>(false, "表单: " + code + " 不存在");
        }

        fd.setName(name);
        fd.setLog(isLog);
        fd.setHasAttachment(hasAttachment);
        fd.setOnlyCamera(isOnlyCamera);
        fd.setFlow(isFlow);
        fd.setFlowTypeCode(flowTypeCode);
        fd.setPageStyle(pageStyle);
        fd.setUnitCode(unitCode);
        fd.setDataSource(dataSource);
        fd.setFormTable(formTable);
        fd.setRelate(isRelate);
        fd.setViewRuleMode(viewRuleMode ? ConstUtil.VIEW_RULE_MODE_EMPTY : ConstUtil.VIEW_RULE_MODE_NORMAL);
        fd.setRendWithoutViewRule(isRendWithoutViewRule);
        return new Result<>(fd.saveForm());
    }

    @ApiOperation(value = "创建表单", notes = "创建表单", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "name", value = "表单名称", required = true, dataType = "String"),
            @ApiImplicitParam(name = "isLog", value = "是否保留置历史记录", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "hasAttachment", value = "是否带有附件", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "isOnlyCamera", value = "手机端只允许拍照，不能选择照片上传", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "isFlow", value = "是否流程型", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "flowTypeCode", value = "流程类型编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "pageStyle", value = "页面风格", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "unitCode", value = "单位编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "viewRuleMode", value = "应用显示规则时清空隐藏元素中的值", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "dataSource", value = "数据源", required = true, dataType = "String"),
            @ApiImplicitParam(name = "formTable", value = "关联的表格", required = true, dataType = "String"),
            @ApiImplicitParam(name = "isRelate", value = "是否关联已有表", required = true, dataType = "Boolean"),
            @ApiImplicitParam(name = "isRendWithoutViewRule", value = "显示规则是否分离渲染", required = true, dataType = "Boolean"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/createForm", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> createForm(@RequestParam String code, @RequestParam String name,
                                     @RequestParam(defaultValue = "false") Boolean isLog, @RequestParam(defaultValue = "false") Boolean hasAttachment, @RequestParam(defaultValue = "false") Boolean isOnlyCamera,
                                     @RequestParam(defaultValue = "true") Boolean isFlow, @RequestParam(defaultValue = "") String flowTypeCode, @RequestParam(defaultValue = "1") Integer pageStyle,
                                     @RequestParam(defaultValue = "root") String unitCode, @RequestParam(defaultValue = "false") Boolean viewRuleMode,
                                     String dataSource, String formTable, @RequestParam(defaultValue = "false") boolean isRelate, @RequestParam(defaultValue = "false") boolean isRendWithoutViewRule
    ) {
        code = code.toLowerCase();
        FormDb fd = new FormDb();
        fd.setCode(code);
        fd.setName(name);
        fd.setLog(isLog);
        fd.setHasAttachment(hasAttachment);
        fd.setOnlyCamera(isOnlyCamera);
        fd.setFlow(isFlow);
        fd.setFlowTypeCode(flowTypeCode);
        fd.setPageStyle(pageStyle);
        fd.setUnitCode(unitCode);
        fd.setContent("");
        fd.setDataSource(dataSource);
        fd.setFormTable(formTable);
        fd.setRelate(isRelate);
        fd.setViewRuleMode(viewRuleMode ? ConstUtil.VIEW_RULE_MODE_EMPTY : ConstUtil.VIEW_RULE_MODE_NORMAL);
        fd.setRendWithoutViewRule(isRendWithoutViewRule);
        fd.createForm();

        // 生成模块
        ModuleSetupDb msd = new ModuleSetupDb();
        msd.getModuleSetupDbOrInit(code);

        return new Result<>();
    }

    @ApiOperation(value = "检查表单内容是否有字段更改", notes = "检查表单内容是否有字段更改", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "schemas", value = "表单中的内容", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin')")
    @ResponseBody
    @RequestMapping(value = "/checkFormSchemas", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> checkFormSchemas(@RequestParam String code, String schemas) {
        JSONArray arr = com.alibaba.fastjson.JSONArray.parseArray(schemas);

        String str = formService.checkFormContent(code, formSchemaUtil.convertToTable(arr.getJSONObject(0)));
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("res", 0);
        json.put("msg", str);
        return new Result<>(json);
    }

    @ApiOperation(value = "保存表单内容", notes = "保存表单内容", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "schemas", value = "表单中的内容", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin')")
    @ResponseBody
    @RequestMapping(value = "/updateFormSchemas", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> updateFormSchemas(@RequestParam String code, String schemas) {
        long t = System.currentTimeMillis();
        FormDb fd = new FormDb();
        fd = fd.getFormDb(code);
        fd.setFormSchemas(schemas);

        JSONArray arr = com.alibaba.fastjson.JSONArray.parseArray(schemas);
        if (arr.size() > 0) {
            fd.setFormParser(null);
            fd.setContent(formSchemaUtil.convertToTable(arr.getJSONObject(0)));
        }
        LogUtil.getLog(getClass()).info("convertToTable: " + (System.currentTimeMillis() - t));
        boolean re = fd.save();
        LogUtil.getLog(getClass()).info("save: " + (System.currentTimeMillis() - t));
        if (re) {
            // 生成模块
            ModuleSetupDb msd = new ModuleSetupDb();
            msd.getModuleSetupDbOrInit(code);

            // 记录日志
            com.redmoon.oa.LogUtil.log(SpringUtil.getUserName(), StrUtil.getIp(request), LogDb.TYPE_ACTION, "FORM_EDIT " + fd.getName() + ":" + fd.getCode());

            // 当表单更新时，处理表单归档记录
            FormArchiveService formArchiveService = SpringUtil.getBean(FormArchiveService.class);
            formArchiveService.onFormUpdate(fd);

            // 重新生成视图
            FormMgr formMgr = new FormMgr();
            re = formMgr.regenerateFormView(fd.getCode());
        }
        LogUtil.getLog(getClass()).info("getModuleSetupDbOrInit: " + (System.currentTimeMillis() - t));
        return new Result<>(re);
    }

    @ApiOperation(value = "获取表单内容", notes = "获取表单内容", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin')")
    @ResponseBody
    @RequestMapping(value = "/getFormSchemas", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> getFormSchemas(@RequestParam String code) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(code);
        JSONArray ary = JSONArray.parseArray(fd.getFormSchemas());
        return new Result<>(ary);
    }

    @ApiOperation(value = "列出提醒（带分页、查询）", notes = "列出提醒（带分页、查询）", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表格编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "page", value = "页码", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageSize", value = "分页大小", required = true, dataType = "Integer"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/listRemind", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> listRemind(@RequestParam String code, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "20") Integer pageSize) {
        String action = ParamUtil.get(request, "action");
        String what = ParamUtil.get(request, "what");
        String orderBy = ParamUtil.get(request, "field");
        if ("".equals(orderBy)) {
            orderBy = "id";
        }
        String sort = ParamUtil.get(request, "order");
        if ("ascend".equals(sort)) {
            sort = "asc";
        } else if ("descend".equals(sort)) {
            sort = "desc";
        } else {
            sort = "desc";
        }

        FormRemindDb frd = new FormRemindDb();
        String sql = "select id from " + frd.getTable().getName() + " where 1=1";
        if (!"".equals(code)) {
            sql += " and table_name=" + StrUtil.sqlstr(FormDb.getTableName(code));
        }

        if ("search".equals(action)) {
            sql += " and name like " + StrUtil.sqlstr("%" + what + "%");
        }
        sql += " order by " + orderBy + " " + sort;

        JSONArray arr = new JSONArray();
        ListResult lr = null;
        try {
            lr = frd.listResult(sql, page, pageSize);
            for (Object o : lr.getResult()) {
                frd = (FormRemindDb) o;
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("id", frd.getInt("id"));
                json.put("name", frd.getString("name"));
                json.put("kind", frd.getInt("kind"));
                String kind = "";
                if (frd.getInt("kind") == FormRemindDb.KIND_EXPIRE) {
                    kind = "到期";
                } else if (frd.getInt("kind") == FormRemindDb.KIND_EVERY_YEAR) {
                    kind = "周年";
                } else if (frd.getInt("kind") == FormRemindDb.KIND_EDIT) {
                    kind = "编辑";
                }
                json.put("kindDesc", kind);
                json.put("dateField", frd.getString("date_field"));
                json.put("aheadDay", frd.getInt("ahead_day"));
                json.put("aheadHour", frd.getInt("ahead_hour"));
                json.put("aheadMinute", frd.getInt("ahead_minute"));
                arr.add(json);
            }
        } catch (ResKeyException e) {
            e.printStackTrace();
        }

        com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
        jsonObject.put("list", arr);
        jsonObject.put("page", page);
        jsonObject.put("total", lr.getTotal());
        return new Result<>(jsonObject);
    }

    @ApiOperation(value = "创建表单提醒", notes = "创建表单提醒", httpMethod = "POST")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/createRemind", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> createRemind() {
        FormRemindMgr frm = new FormRemindMgr();
        FormRemindDb frd = new FormRemindDb();
        return new Result<>(frm.create(request, frd, "form_remind_create"));
    }

    @ApiOperation(value = "编辑表单提醒", notes = "编辑表单提醒", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/editRemindPage", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> editRemindPage(@RequestParam Integer id) {
        FormRemindDb frd = new FormRemindDb();
        frd = (FormRemindDb) frd.getQObjectDb(id);

        String[] roleAry = StrUtil.split(frd.getString("roles"), ",");
        int ulen = 0;
        if (roleAry != null) {
            ulen = roleAry.length;
        }

        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        String roleCode, desc;
        String roleCodes = "";
        String descs = "";
        RoleDb rd = new RoleDb();
        for (int i = 0; i < ulen; i++) {
            rd = rd.getRoleDb(roleAry[i]);
            roleCode = rd.getCode();
            desc = rd.getDesc();
            if ("".equals(roleCodes)) {
                roleCodes += roleCode;
            } else {
                roleCodes += "," + roleCode;
            }
            if ("".equals(descs)) {
                descs += desc;
            } else {
                descs += "," + desc;
            }
        }
        json.put("roleCodes", roleCodes);
        json.put("roleDescs", descs);

        String users = "", userRealNames = "";
        String[] aryusers = StrUtil.split(frd.getString("users"), ",");
        if (aryusers != null) {
            int len = aryusers.length;
            UserDb ud = new UserDb();
            for (int i = 0; i < len; i++) {
                if ("".equals(users)) {
                    users = aryusers[i];
                    ud = ud.getUserDb(aryusers[i]);
                    userRealNames = ud.getRealName();
                } else {
                    users += "," + aryusers[i];
                    ud = ud.getUserDb(aryusers[i]);
                    userRealNames += "," + ud.getRealName();
                }
            }
        }
        json.put("userNames", users);
        json.put("userRealNames", userRealNames);
        json.put("id", frd.getString("id"));
        json.put("name", frd.getString("name"));
        json.put("tableName", frd.getString("table_name"));
        json.put("dateField", frd.getString("date_field"));
        json.put("userField", frd.getString("user_field"));
        json.put("filter", frd.getString("filter"));
        json.put("kind", frd.getString("kind"));
        json.put("aheadDay", frd.getInt("ahead_day"));
        json.put("aheadHour", frd.getInt("ahead_hour"));
        json.put("aheadMinute", frd.getInt("ahead_minute"));
        json.put("title", frd.getString("title"));
        json.put("isMsg", frd.getInt("is_msg"));
        json.put("isEmail", frd.getInt("is_email"));
        json.put("isSms", frd.getInt("is_sms"));
        json.put("content", frd.getString("content"));
        json.put("actionType", StrUtil.getNullStr(frd.getString("action_type")));
        return new Result<>(json);
    }

    @ApiOperation(value = "保存表单提醒", notes = "保存表单提醒", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "ID", required = true, dataType = "Integer"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin')")
    @ResponseBody
    @RequestMapping(value = "/updateRemind", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> updateRemind(@RequestParam Integer id) {
        FormRemindDb frd = new FormRemindDb();
        frd = (FormRemindDb)frd.getQObjectDb(id);
        boolean re;
        try {
            FormRemindMgr frm = new FormRemindMgr();
            re = frm.save(request, frd, "form_remind_edit");
        }
        catch (ErrMsgException e) {
            return new Result<>(false, e.getMessage());
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "删除表单提醒", notes = "删除表单提醒", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "ID，以逗号分隔", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin')")
    @ResponseBody
    @RequestMapping(value = "/delRemind", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> delRemind(@RequestParam String ids) {
        String[] ary = StrUtil.split(ids, ",");
        FormRemindDb frd = new FormRemindDb();
        for (String s : ary) {
            frd = (FormRemindDb) frd.getQObjectDb(StrUtil.toLong(s));
            try {
                frd.del();
            } catch (ResKeyException e) {
                return new Result<>(false, e.getMessage(request));
            }
        }
        return new Result<>();
    }

    @ApiOperation(value = "取得显示规则", notes = "取得显示规则", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getViewRule", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> getViewRule(String formCode) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);

        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("views", fd.getViewSetup());
        return new Result<>(json);
    }

    @ApiOperation(value = "保存显示规则", notes = "保存显示规则", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "viewSetup", value = "显示规则", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/updateViewSetup", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> updateViewSetup(String formCode, String viewSetup) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        fd.setViewSetup(viewSetup);
        return new Result<>(fd.save());
    }

    @ApiOperation(value = "取得验证规则", notes = "取得验证规则", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getCheckSetup", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> getCheckSetup(String formCode) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);

        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("checkSetup", fd.getCheckSetup());
        return new Result<>(json);
    }

    @ApiOperation(value = "保存验证规则", notes = "保存验证规则", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "checkSetup", value = "验证规则", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/updateCheckSetup", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> updateCheckSetup(String formCode, String checkSetup) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        fd.setCheckSetup(checkSetup);
        return new Result<>(fd.save());
    }

    @ApiOperation(value = "进入回写页面", notes = "进入回写页面", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getWriteBackPage", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> getWriteBackPage(String formCode) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        String writeProp = fd.getWriteProp();
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("writeProp", writeProp);
        return new Result<>(json);
    }

    @ApiOperation(value = "进入回写外部数据页面", notes = "进入回写外部数据页面", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/getWriteBackPageByDb", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> getWriteBackPageByDb(String formCode) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        String writeProp = fd.getWriteDbProp();
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("writeProp", writeProp);
        return new Result<>(json);
    }

    @ApiOperation(value = "保存回写", notes = "保存回写", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "writeProp", value = "回写属性", required = true, dataType = "String")
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/updateWriteBack", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> updateWriteBack(String formCode, String writeProp) {
        boolean re;
        try {
            FormDb fd = new FormDb();
            fd = fd.getFormDb(formCode);
            fd.setWriteProp(writeProp);
            re = fd.save();
        } catch (ErrMsgException e) {
            LogUtil.getLog(getClass()).error(e);
            return new Result<>(false);
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "保存外部数据源回写", notes = "保存外部数据源回写", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "writeProp", value = "回写属性", required = true, dataType = "String")
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/updateWriteBackByDb", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> updateWriteBackByDb(@RequestParam(value = "formCode") String formCode, @RequestParam(value = "writeProp") String writeProp) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        fd.setWriteDbProp(writeProp);
        return new Result<>(fd.save());
    }

    @ApiOperation(value = "清除回写", notes = "清除回写", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/clearWriteBack", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> clearWriteBack(@RequestParam(value = "formCode") String formCode) {
        boolean re = true;
        Element del = null;
        String scene = ParamUtil.get(request, "scene");
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        String writeProp = fd.getWriteProp();
        if (writeProp != null && !"".equals(writeProp)) {
            SAXBuilder parser = new SAXBuilder();
            org.jdom.Document doc = null;
            Element root = null;
            Element child = null;
            List list = null;
            Iterator ir = null;
            try {
                doc = parser.build(new InputSource(new StringReader(writeProp)));
                root = doc.getRootElement();
                list = root.getChildren("node");
                if (list != null) {
                    ir = list.iterator();
                }
                while (ir != null && ir.hasNext()) {
                    Element nodeFinish = (Element) ir.next();
                    String internalNodeText = nodeFinish.getChildText("scene");
                    if (scene.equals(internalNodeText)) {                             //节点存在先删除
                        del = nodeFinish;
                    }
                }
                if (del != null) {
                    root.removeContent(del);
                }

                Format format = Format.getPrettyFormat();
                ByteArrayOutputStream byteRsp = new ByteArrayOutputStream();
                XMLOutputter xmlOut = new XMLOutputter(format);
                xmlOut.output(doc, byteRsp);
                writeProp = byteRsp.toString("utf-8");
                byteRsp.close();
                fd.setWriteProp(writeProp);
                re = fd.save();
            } catch (JDOMException | IOException e) {
                LogUtil.getLog(getClass()).error(e);
            }
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "清除外部回写", notes = "清除外部回写", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/clearWriteBackByDb", method = RequestMethod.POST, produces = {"text/html;", "application/json;charset=UTF-8;"})
    public Result<Object> clearWriteBackByDb(@RequestParam(value = "formCode") String formCode) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        SAXBuilder parser = new SAXBuilder();
        String writeProp = fd.getWriteDbProp();

        boolean re = true;
        String scene = ParamUtil.get(request, "scene");
        if (StrUtil.isEmpty(scene)) {
            scene = "add";
        }
        Element del = null;
        String timestamp = ParamUtil.get(request, "timestamp");
        if (writeProp != null && !"".equals(writeProp)) {
            try {
                org.jdom.Document doc = null;
                Element root = null;
                Element child = null;
                List list = null;
                Iterator ir = null;
                doc = parser.build(new InputSource(new StringReader(writeProp)));
                root = doc.getRootElement();

                list = root.getChildren("node");
                if (list != null) {
                    ir = list.iterator();
                }
                while (ir != null && ir.hasNext()) {
                    Element nodeFinish = (Element) ir.next();
                    String internalNodeText = nodeFinish.getChildText("scene");
                    if (scene.equals(internalNodeText)) {                             //节点存在先删除
                        del = nodeFinish;
                    }
                }
                if (del != null) {
                    root.removeContent(del);
                }

                Format format = Format.getPrettyFormat();
                ByteArrayOutputStream byteRsp = new ByteArrayOutputStream();
                XMLOutputter xmlOut = new XMLOutputter(format);
                xmlOut.output(doc, byteRsp);
                writeProp = byteRsp.toString("utf-8");
                byteRsp.close();
                fd.setWriteDbProp(writeProp);
                re = fd.save();
            } catch (JDOMException | IOException e) {
                LogUtil.getLog(getClass()).error(e);
            }
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "检查ueditor编辑的表单内容", notes = "检查ueditor编辑的表单内容", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "content", value = "表单内容", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin')")
    @ResponseBody
    @RequestMapping(value = "/checkUeditorForm", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> checkUeditorForm(@RequestParam String formCode, String content) {
        String str = formService.checkFormContent(formCode, content);
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("res", 0);
        json.put("msg", str);
        return new Result<>(json);
    }

    @ApiOperation(value = "保存ueditor编辑的表单内容", notes = "保存ueditor编辑的表单内容", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "formCode", value = "表单编码", required = true, dataType = "String"),
            @ApiImplicitParam(name = "content", value = "表单内容", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/updateUeditorForm", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> updateUeditorForm(@RequestParam String formCode, @RequestParam String content) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);
        fd.setContent(content);
        Config cfg = Config.getInstance();

        if (cfg.getInt("formEditorType") == 2 && licenseUtil.isEnabled(LicenseConstUtil.FORM_DESIGNER)) {
            fd.setFormSchemas(formProUtil.parseTable(formCode, content));
        }
        boolean re = fd.save();
        if (re) {
            // 自动生成模块
            ModuleSetupDb msd = new ModuleSetupDb();
            msd.getModuleSetupDbOrInit(formCode);
            // 记录日志
            com.redmoon.oa.LogUtil.log(SpringUtil.getUserName(), StrUtil.getIp(request), LogDb.TYPE_ACTION, "FORM_EDIT " + fd.getName() + ":" + fd.getCode());

            // 当表单更新时，处理表单归档记录
            FormArchiveService formArchiveService = SpringUtil.getBean(FormArchiveService.class);
            formArchiveService.onFormUpdate(fd);

            // 重新生成视图
            FormMgr formMgr = new FormMgr();
            re = formMgr.regenerateFormView(fd.getCode());
        }
        return new Result<>(re);
    }

    @ApiOperation(value = "取表单内容", notes = "取表单内容", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "表单编码", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/getFormContent", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> getFormContent(@RequestParam String formCode) {
        FormDb fd = new FormDb();
        fd = fd.getFormDb(formCode);

        com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
        jsonObject.put("content", fd.getContent());
        return new Result<>(jsonObject);
    }

    @ApiOperation(value = "列出系统中的所有表", notes = "列出系统中的所有表", httpMethod = "POST")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/listTable", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> listTable() {
        JSONArray ary = new JSONArray();
        JdbcTemplate jt = new JdbcTemplate();
        java.sql.Connection conn = jt.getConnection().getCon();
        try {
            DatabaseMetaData metaData = conn.getMetaData();
            FormDb fd = new FormDb();
            if (Global.db.equals(Global.DB_MYSQL)) {
                ResultSet rs = metaData.getTables(conn.getCatalog(), "root", null, new String[]{"TABLE"});
                while (rs.next()) {
                    String tableName = rs.getString("TABLE_NAME");
                    com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                    if (!tableName.toUpperCase().endsWith("_LOG")) {
                        if (tableName.toUpperCase().startsWith("ft_")) {
                            String formCode = FormDb.getCodeByTableName(tableName);
                            fd = fd.getFormDb(formCode);
                            if (fd.isLoaded()) {
                                tableName += "(" + fd.getName() + ")";
                            }
                        }
                        json.put("code", tableName);
                        json.put("name", tableName);
                        ary.add(json);
                    }
                }
            } else if (Global.db.equals(Global.DB_ORACLE)) {
                ResultSet rs = metaData.getTables(conn.getCatalog(), "SCOTT", null, new String[]{"TABLE"});
                while (rs.next()) {
                    String tableName = rs.getString("TABLE_NAME");
                    com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                    if (!tableName.toUpperCase().endsWith("_LOG")) {
                        if (tableName.toUpperCase().startsWith("ft_")) {
                            String formCode = FormDb.getCodeByTableName(tableName);
                            fd = fd.getFormDb(formCode);
                            if (fd.isLoaded()) {
                                tableName += "(" + fd.getName() + ")";
                            }
                        }
                        json.put("code", tableName);
                        json.put("name", tableName);
                        ary.add(json);
                    }
                }
            } else {
                ResultSet rs = metaData.getTables(conn.getCatalog(), "SCOTT", null, new String[]{"TABLE"});
                while (rs.next()) {
                    String tableName = rs.getString("TABLE_NAME");
                    com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                    if (!tableName.toUpperCase().endsWith("_LOG")) {
                        if (tableName.toUpperCase().startsWith("ft_")) {
                            String formCode = FormDb.getCodeByTableName(tableName);
                            fd = fd.getFormDb(formCode);
                            if (fd.isLoaded()) {
                                tableName += "(" + fd.getName() + ")";
                            }
                        }
                        json.put("code", tableName);
                        json.put("name", tableName);
                        ary.add(json);
                    }
                }
            }
        } catch (SQLException e) {
            return new Result<>(false, e.getMessage());
        } finally {
            try {
                conn.close();
            } catch (SQLException e) {
                return new Result<>(false, e.getMessage());
            }
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "取表中的字段", notes = "取表中的字段", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "tableName", value = "表名", required = true, dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @PreAuthorize("hasAnyAuthority('admin.flow', 'admin', 'admin.unit')")
    @ResponseBody
    @RequestMapping(value = "/getTableFields", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8;"})
    public Result<Object> getTableFields(@RequestParam String tableName) {
        JSONArray ary = new JSONArray();
        String sql = "select * from " + tableName;
        String dbSource = Global.getDefaultDB();
        com.cloudwebsoft.framework.db.Connection conn = new com.cloudwebsoft.framework.db.Connection(dbSource);
        try {
            conn.setMaxRows(1); //尽量减少内存的使用
            ResultSet rs = conn.executeQuery(sql);
            ResultSetMetaData rm = rs.getMetaData();
            int colCount = rm.getColumnCount();

            String fieldName = "";
            FormDb fd = new FormDb();

            if (tableName.toUpperCase().startsWith("ft_")) {
                String formCode = FormDb.getCodeByTableName(tableName);
                fd = fd.getFormDb(formCode);
            }

            for (int i = 1; i <= colCount; i++) {
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                fieldName = rm.getColumnName(i);
                if (fd.isLoaded()) {
                    FormField ff = fd.getFormField(fieldName);
                    if (ff!=null) {
                        fieldName = ff.getTitle();
                    }
                }
                json.put("field", rm.getColumnName(i));
                json.put("name", fieldName);
                ary.add(json);
            }
        } catch (SQLException e) {
            return new Result<>(false, e.getMessage());
        } finally {
            conn.close();
        }
        return new Result<>(ary);
    }

    @ApiOperation(value = "导出解决方案", notes = "导出解决方案", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "String", value = "表单编码，以逗号分隔", dataType = "String"),
    })
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @RequestMapping(value = "/exportSolution", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public void exportSolution(HttpServletResponse response, @RequestParam(value = "codes") String codes) throws IOException, ErrMsgException {
        response.setContentType(MIMEMap.get("txt"));
        //response.setHeader("Content-disposition","filename="+att.getName());
        response.setHeader("Content-disposition", "attachment; filename=" + StrUtil.GBToUnicode("解决方案") + ".txt");

        // response.setContentType("application/octet-stream");
        // response.setHeader("Content-disposition","attachment; filename="+att.getName());
        String content = ModuleUtil.exportSolution(codes);

        try (BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {

            byte[] buff = new byte[2048];
            int bytesRead;

            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        } catch (final IOException e) {
            LogUtil.getLog(getClass()).error(e);
        }
    }

    @ApiOperation(value = "导入解决方案", notes = "导出解决方案", httpMethod = "POST")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ResponseBody
    @RequestMapping(value = "/importSolution", method = RequestMethod.POST, produces = {"text/html;charset=UTF-8;", "application/json;charset=UTF-8;"})
    public Result<Object> importSolution() throws ErrMsgException {
        ModuleUtil.importSolution(request.getServletContext(), request);
        return new Result<>();
    }
}
